package com.example.lotterysystem.service.impl;

import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtils;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.controller.param.CreateActivityParam;
import com.example.lotterysystem.controller.param.CreatePrizeByActivityParam;
import com.example.lotterysystem.controller.param.CreateUserByActivityParam;
import com.example.lotterysystem.controller.param.PageListParam;
import com.example.lotterysystem.dao.dataobject.ActivityDO;
import com.example.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.example.lotterysystem.dao.dataobject.ActivityUserDO;
import com.example.lotterysystem.dao.dataobject.PrizeDO;
import com.example.lotterysystem.dao.mapper.*;
import com.example.lotterysystem.service.ActivityService;
import com.example.lotterysystem.service.dto.ActivityDTO;
import com.example.lotterysystem.service.dto.ActivityDetailDTO;
import com.example.lotterysystem.service.dto.CreateActivityDTO;
import com.example.lotterysystem.service.dto.PageListDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import com.example.lotterysystem.service.enums.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class ActivityServiceImpl implements ActivityService {
    private static final Logger logger = LoggerFactory.getLogger(ActivityServiceImpl.class);

    //为了区别业务
    private final String ACTIVITY_PREFIX = "ACTIVITY_";
    //设置过期时间
    private final Long ACTIVITY_TIMEOUT = 60 * 60 * 24 *3L;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Override
    @Transactional(rollbackFor = Exception.class) //涉及多表
    public CreateActivityDTO createActivtiy(CreateActivityParam param) {
        //校验参数
        checkActivityParam(param);
        //保存活动信息
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);

        //保存关联奖品信息
        List<CreatePrizeByActivityParam> activityPrizeLists = param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList = activityPrizeLists
                .stream()
                .map(activityPrizeList ->{
                    ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeId(activityPrizeList.getPrizeId());
                    activityPrizeDO.setPrizeAmount(activityPrizeList.getPrizeAmount());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    activityPrizeDO.setPrizeTiers(activityPrizeList.getPrizeTiers());
                    return activityPrizeDO;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);

        //保存关联人员信息
        List<CreateUserByActivityParam> activityUserLists = param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList = activityUserLists
                .stream()
                .map(activityUserList ->{
                    ActivityUserDO activityUserDO = new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserId(activityUserList.getUserId());
                    activityUserDO.setUserName(activityUserList.getUserName());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDO;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDOList);

        //整合全部信息 保存到redis

        //1.获取奖品的基本属性 先得获取需要查询的奖品id
        List<Long> prizeIds = param.getActivityPrizeList()
                .stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
        ActivityDetailDTO detailDTO = convertToActivityDetailDTO(activityDO,activityPrizeDOList,prizeDOList,activityUserDOList);
        // 缓存到redis中
        cacheActivityDetailDTO(detailDTO);

        //构造返回参数
        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());
        return createActivityDTO;
    }

    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageListParam param) {
        int count = activityMapper.count();
        List<ActivityDO> activityDOList = activityMapper.selectActivityList(param.offset(),param.getPageSize());
        List<ActivityDTO> activityDTOList = activityDOList
                .stream()
                .map(activityDO -> {
                    ActivityDTO activityDTO = new ActivityDTO();
                    activityDTO.setActivityId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setDescription(activityDO.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
                    return activityDTO;
                } ).collect(Collectors.toList());
        return new PageListDTO<>(count,activityDTOList);
    }

    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        if(null == activityId) {
            logger.warn("获取活动详细信息失败！");
            return null;
        }
        //查询redis
        ActivityDetailDTO activityDetailDTO = getActivityFromCache(activityId);
        if(null != activityDetailDTO) {
            return activityDetailDTO;
        }
        //如果redis不存在 查表
        //活动表
        ActivityDO aDO = activityMapper.selectById(activityId);
        //活动人员表
        List<ActivityUserDO> auDO = activityUserMapper.selectByActivityId(activityId);
        //活动奖品表
        List<ActivityPrizeDO> apDO = activityPrizeMapper.selectByActivityId(activityId);
        //奖品表
        List<Long> prizeIds = apDO.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDO = prizeMapper.batchSelectByIds(prizeIds);
        //重新整合到redis中
        activityDetailDTO = convertToActivityDetailDTO(aDO,apDO,pDO,auDO);
        cacheActivityDetailDTO(activityDetailDTO);
        //返回
        return activityDetailDTO;
    }

    @Override
    public void cacheActivity(Long activityId) {
        if(null == activityId) {
            logger.info("要缓存的activityId为空！ ");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }
        //活动表
        ActivityDO aDO = activityMapper.selectById(activityId);
        if(null == aDO) {
            logger.error("要缓存的活动Id错误");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);
        }
        //活动人员表
        List<ActivityUserDO> auDO = activityUserMapper.selectByActivityId(activityId);
        //活动奖品表
        List<ActivityPrizeDO> apDO = activityPrizeMapper.selectByActivityId(activityId);
        //奖品表
        List<Long> prizeIds = apDO.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDO = prizeMapper.batchSelectByIds(prizeIds);
        //重新整合到redis中
        ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(aDO,apDO,pDO,auDO);
        cacheActivityDetailDTO(activityDetailDTO);

    }

    /**
     * 缓存数据到redis
     * @param detailDTO
     */
    private void cacheActivityDetailDTO(ActivityDetailDTO detailDTO) {
        // key: ACTIVITY_activityId
        // value: detailDTO(json)
        if(null == detailDTO || null == detailDTO.getActivityId()) {
            logger.warn("要缓存的活动信息为空！");
            return;
        }
        try {
            redisUtil.set(ACTIVITY_PREFIX+detailDTO.getActivityId(),
                    JacksonUtils.writeValueString(detailDTO),
                    ACTIVITY_TIMEOUT);
        }catch (Exception e) {
            logger.error("缓存活动异常 ActivityDetailDTO{}",
                    JacksonUtils.writeValueString(detailDTO),
                    e);
        }
    }

    /**
     * 从缓存中获取数据
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId) {
        if(null == activityId) {
            logger.warn("获取缓存活动数据的activityId为空");
            return null;
        }
        try {
            String str = redisUtil.get(ACTIVITY_PREFIX+activityId);
            if(!StringUtils.hasText(str)) {
                logger.info("获取的缓存活动数据为空 key={}",ACTIVITY_PREFIX+activityId);
                return null;
            }
            return JacksonUtils.readValue(str,ActivityDetailDTO.class);
        }catch(Exception e) {
            logger.error("从缓存中获取活动数据异常，key={}",ACTIVITY_PREFIX+activityId,e);
            return null;
        }

    }

    /**
     * 根据基本DO整合完整的活动信息ActivityDetailDTO
     * @param activityDO
     * @param activityPrizeDOList
     * @param prizeDOList
     * @param activityUserDOList
     * @return
     */
    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                                         List<ActivityPrizeDO> activityPrizeDOList,
                                                         List<PrizeDO> prizeDOList,
                                                         List<ActivityUserDO> activityUserDOList) {
        ActivityDetailDTO detailDTO = new ActivityDetailDTO();
        detailDTO.setActivityId(activityDO.getId());
        detailDTO.setActivityName(activityDO.getActivityName());
        detailDTO.setDescription(activityDO.getDescription());
        detailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));


        //设置奖品信息
        //apDO {prizeId,status,amount} {prizeId,status,amount} {prizeId,status,amount}
        //pDO {prizeId,name,url,....} {prizeId,name,url,....}  {prizeId,name,url,....}
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = activityPrizeDOList
                .stream()
                .map(apDO -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setId(apDO.getPrizeId());

                    Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream()
                            .filter(prizeDO -> prizeDO.getId().equals(apDO.getPrizeId()))
                            .findFirst();
                    optionalPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setDescription(prizeDO.getDescription());
                    });
                    prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                    prizeDTO.setPrizeTiers(ActivityPrizeTiersEnum.forName(apDO.getPrizeTiers()));
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(apDO.getStatus()));
                    return prizeDTO;
                }).collect(Collectors.toList());

        detailDTO.setPrizeDoList(prizeDTOList);

        //设置人员信息
        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDOList
                .stream()
                .map(auDO->{
                    ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                    userDTO.setUserId(auDO.getUserId());
                    userDTO.setUserName(auDO.getUserName());
                    userDTO.setStatus(ActivityUserStatusEnum.forName(auDO.getStatus()));
                    return userDTO;
                }).collect(Collectors.toList());
        detailDTO.setUserDTOList(userDTOList);

        return detailDTO;

    }

    /**
     * 校验参数
     * @param param
     */
    private void checkActivityParam(CreateActivityParam param) {
        if(null == param) {
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }

        //用户id在奖品表中是否存在
        // 1 2 3  ->  1 2
        List<Long> userIds = param.getActivityUserList()
                .stream()
                .map(CreateUserByActivityParam::getUserId)
                .distinct()//去重
                .collect(Collectors.toList());
        List<Long> existUserIds  = userMapper.selectExistByIds(userIds);
        if(CollectionUtils.isEmpty(existUserIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        userIds.forEach(id -> {
            if(!existUserIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });
        //奖品id在奖品表中是否存在

        List<Long> prizeIds = param.getActivityPrizeList()
                .stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existPrizeIds = prizeMapper.selectExistByIds(prizeIds);
        if(CollectionUtils.isEmpty(existPrizeIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        prizeIds.forEach(id-> {
            if(!existPrizeIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });

        //人员数量大于等于奖品数量
        int userAmount = param.getActivityUserList().size();
        long prizeAmount = param.getActivityPrizeList().
                stream().
                mapToLong(CreatePrizeByActivityParam::getPrizeAmount)
                .sum();
        if(userAmount < prizeAmount) {
            throw new ServiceException(ServiceErrorCodeConstants.PRIZE_USER_AMOUNT_ERROR);
        }

        //校验活动奖品是否有效
        param.getActivityPrizeList().forEach(prize ->{
            if(null == ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });

    }
}
